package edu.ksu.cis.indus.slicer;



import edu.ksu.cis.indus.common.collections.CollectionsUtilities;
import edu.ksu.cis.indus.common.datastructures.Pair;
import edu.ksu.cis.indus.common.datastructures.Triple;
import edu.ksu.cis.indus.common.soot.BasicBlockGraph;
import edu.ksu.cis.indus.common.soot.BasicBlockGraph.BasicBlock;
import edu.ksu.cis.indus.common.soot.BasicBlockGraphMgr;
import edu.ksu.cis.indus.interfaces.ICallGraphInfo;
import edu.ksu.cis.indus.interfaces.ICallGraphInfo.CallTriple;
import edu.ksu.cis.indus.interfaces.INewExpr2InitMapper;
import edu.ksu.cis.indus.processing.Context;
import edu.ksu.cis.indus.staticanalyses.dependency.IDependencyAnalysis;
import java.util.BitSet;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.Stack;
import org.apache.commons.collections.Closure;
import org.apache.commons.collections.MapUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import soot.Local;
import soot.SootClass;
import soot.SootMethod;
import soot.Value;
import soot.ValueBox;
import soot.jimple.AssignStmt;
import soot.jimple.DefinitionStmt;
import soot.jimple.IdentityStmt;
import soot.jimple.InstanceInvokeExpr;
import soot.jimple.InvokeExpr;
import soot.jimple.InvokeStmt;
import soot.jimple.NewExpr;
import soot.jimple.ParameterRef;
import soot.jimple.ReturnStmt;
import soot.jimple.Stmt;
import soot.jimple.ThrowStmt;
import soot.toolkits.graph.CompleteUnitGraph;
import soot.toolkits.scalar.SimpleLocalDefs;
import soot.toolkits.scalar.SimpleLocalUses;
import soot.toolkits.scalar.UnitValueBoxPair;

public class BackwardSlicingPart
  implements IDirectionSensitivePartOfSlicingEngine
{
  private static final Logger LOGGER = LoggerFactory.getLogger(BackwardSlicingPart.class);
  final SlicingEngine engine;
  private final Map callee2callsites = new HashMap();

  private final Map exitTransformedMethods = new HashMap();

  private final Map method2params = new HashMap();

  private final Closure returnValueInclClosure = new ReturnValueInclusionClosure();

  private final Closure tailStmtInclusionClosure = new TailStmtInclusionClosure();

  BackwardSlicingPart(SlicingEngine theEngine)
  {
    this.engine = theEngine;
  }

  public boolean continueProcessing()
  {
    if (LOGGER.isDebugEnabled()) {
      LOGGER.debug("continueProcessing() - BEGIN");
    }

    boolean _result = false;

    for (Iterator _i = this.callee2callsites.entrySet().iterator(); _i.hasNext(); ) {
      Map.Entry _entry = (Map.Entry)(Map.Entry)_i.next();
      SootMethod callee = (SootMethod)_entry.getKey();
      BitSet _params = (BitSet)this.method2params.get(callee);
      if (_params != null) {
        Collection _temp = (Collection)(Collection)_entry.getValue();
        boolean _remove = _params.cardinality() == callee.getParameterCount();
        for (Iterator _k = _temp.iterator(); _k.hasNext(); ) {
          Triple _triple = (Triple)(Triple)_k.next();
          Stmt _stmt = (Stmt)_triple.getFirst();
          SootMethod _caller = (SootMethod)_triple.getSecond();
          Stack _stack = (Stack)(Stack)_triple.getThird();
          InvokeExpr _expr = _stmt.getInvokeExpr();
          for (int _j = _params.nextSetBit(0); _j >= 0; _j = _params.nextSetBit(_j + 1)) {
            _result |= this.engine.generateExprLevelSliceCriterion(_expr.getArgBox(_j), _stmt, _caller, true, _stack);
          }
          if (_remove) {
            _k.remove();
          }
        }
        if (_temp.isEmpty()) {
          _i.remove();
        }
      }
    }

    if (LOGGER.isDebugEnabled()) {
      LOGGER.debug("continueProcessing() - END");
    }

    return _result;
  }

  public void generateCriteriaForTheCallToMethod(SootMethod callee, SootMethod caller, Stmt callStmt)
  {
    if (LOGGER.isDebugEnabled()) {
      LOGGER.debug("generateCriteriaForTheCallToMethod(Stmt callStmt = " + callStmt + "SootMethod callee = " + callee + 
        ", SootMethod caller = " + caller + ", stack = " + this.engine.getCopyOfCallStackCache() + ") - BEGIN");
    }

    this.engine.generateStmtLevelSliceCriterion(callStmt, caller, false);
    this.engine.getCollector().includeInSlice(callStmt.getInvokeExprBox());
    generateCriteriaForReceiverOfAt(callStmt, caller);
    recordCallInfoForProcessingArgsTo(callStmt, caller, callee);

    if (LOGGER.isDebugEnabled())
      LOGGER.debug("generateCriteriaForTheCallToMethod() - END");
  }

  public void generateCriteriaToIncludeCallees(Stmt stmt, SootMethod caller, Collection callees)
  {
    if (LOGGER.isDebugEnabled()) {
      LOGGER.debug("generateCriteriaToIncludeCallees(Stmt stmt = " + stmt + ", Collection callees = " + callees + 
        ", SootMethod caller = " + caller + ", stack = " + this.engine.getCopyOfCallStackCache() + ") - BEGIN");
    }

    processTailsOf(callees, stmt, caller, this.tailStmtInclusionClosure);

    if (LOGGER.isDebugEnabled())
      LOGGER.debug("generateCriteriaToIncludeCallees() - END");
  }

  public Collection getDependences(IDependencyAnalysis analysis, Object entity, SootMethod method)
  {
    Collection _result = new HashSet();
    Object _direction = analysis.getDirection();

    if ((_direction.equals(IDependencyAnalysis.BACKWARD_DIRECTION)) || 
      (_direction.equals(IDependencyAnalysis.BI_DIRECTIONAL)))
      _result.addAll(analysis.getDependees(entity, method));
    else if (LOGGER.isWarnEnabled()) {
      LOGGER.warn("Trying to retrieve BACKWARD dependence from a dependence analysis that is FORWARD direction. -- " + 
        analysis.getClass() + " - " + _direction);
    }

    return _result;
  }

  public void processLocalAt(Local local, Stmt stmt, SootMethod method)
  {
    if (LOGGER.isDebugEnabled()) {
      LOGGER.debug("processLocalAt(Local local = " + local + ", Stmt stmt = " + stmt + ", SootMethod method = " + 
        method + ", stack = " + this.engine.getCopyOfCallStackCache() + ") - BEGIN");
    }

    this.engine.generateStmtLevelSliceCriterion(stmt, method, true);

    if (stmt.containsInvokeExpr()) {
      Iterator _i = stmt.getDefBoxes().iterator();
      int _iEnd = stmt.getDefBoxes().size();

      for (int _iIndex = 0; _iIndex < _iEnd; ++_iIndex) {
        ValueBox _vb = (ValueBox)_i.next();
        Value _v = _vb.getValue();

        if (_v.equals(local)) {
          Context _ctxt = new Context();
          _ctxt.setRootMethod(method);
          _ctxt.setStmt(stmt);
          processTailsOf(this.engine.getCgi().getCallees(stmt.getInvokeExpr(), _ctxt), stmt, method, 
            this.returnValueInclClosure);
          break;
        }
      }
    }

    if (LOGGER.isDebugEnabled())
      LOGGER.debug("processLocalAt() , stack = " + this.engine.getCopyOfCallStackCache() + "- END");
  }

  public void processNewExpr(Stmt stmt, SootMethod method)
  {
    if (LOGGER.isDebugEnabled()) {
      LOGGER.debug("processNewExpr(Stmt stmt = " + stmt + ", SootMethod method = " + method + ", stack = " + 
        this.engine.getCopyOfCallStackCache() + ") - BEGIN");
    }

    if (stmt instanceof AssignStmt) {
      AssignStmt _as = (AssignStmt)stmt;

      if (_as.getRightOp() instanceof NewExpr) {
        Stmt _def = this.engine.getInitMapper().getInitCallStmtForNewExprStmt(stmt, method);
        if (_def != null)
          this.engine.generateStmtLevelSliceCriterion(_def, method, true);
        else if (LOGGER.isWarnEnabled()) {
          LOGGER.warn("Could not find the <init> for " + stmt + "@" + method);
        }
      }
    }

    if (LOGGER.isDebugEnabled())
      LOGGER.debug("processNewExpr() - END");
  }

  public void processParameterRef(IdentityStmt stmt, SootMethod callee)
  {
    if (LOGGER.isDebugEnabled()) {
      LOGGER.debug("processParameterRef(ValueBox pBox = " + stmt.getRightOpBox() + ", SootMethod callee = " + callee + 
        ", stack = " + this.engine.getCopyOfCallStackCache() + ") - BEGIN");
    }

    ParameterRef _param = (ParameterRef)stmt.getRightOp();
    int _index = _param.getIndex();

    BitSet _params = (BitSet)this.method2params.get(callee);

    if (_params == null)
    {
      int _maxArguments = 8;
      _params = new BitSet(8);
      this.method2params.put(callee, _params);
    }
    _params.set(_param.getIndex());

    if (LOGGER.isDebugEnabled()) {
      LOGGER.debug("Parameters required for " + callee + " are " + _params);
    }

    if (this.engine.ifInsideContext()) {
      ICallGraphInfo.CallTriple _temp = this.engine.returnFromMethod();
      if (_temp != null) {
        SootMethod _caller = _temp.getMethod();
        Stmt _stmt = _temp.getStmt();
        ValueBox _argBox = _temp.getExpr().getArgBox(_index);
        this.engine.generateExprLevelSliceCriterion(_argBox, _stmt, _caller, true);
        generateCriteriaForReceiverOfAt(_stmt, _caller);
      }
      this.engine.enterMethod(_temp);
    } else {
      for (Iterator _i = this.engine.getCgi().getCallers(callee).iterator(); _i.hasNext(); ) {
        ICallGraphInfo.CallTriple _ctrp = (ICallGraphInfo.CallTriple)_i.next();
        SootMethod _caller = _ctrp.getMethod();
        Stmt _stmt = _ctrp.getStmt();
        ValueBox _argBox = _ctrp.getExpr().getArgBox(_index);
        this.engine.generateExprLevelSliceCriterion(_argBox, _stmt, _caller, true);
        generateCriteriaForReceiverOfAt(_stmt, _caller);
      }
    }

    if (LOGGER.isDebugEnabled())
      LOGGER.debug("processParameterRef() - END");
  }

  public void reset()
  {
    this.callee2callsites.clear();
  }

  public Collection retrieveValueBoxesToTransformExpr(ValueBox valueBox, Stmt stmt)
  {
    Collection _valueBoxes = new HashSet();
    _valueBoxes.add(valueBox);

    Value _value = valueBox.getValue();

    if (!(_value instanceof InvokeExpr))
      _valueBoxes.addAll(_value.getUseBoxes());
    else if (_value instanceof InstanceInvokeExpr) {
      _valueBoxes.add(((InstanceInvokeExpr)_value).getBaseBox());
    }

    if ((stmt instanceof DefinitionStmt) && (stmt.getDefBoxes().contains(valueBox))) {
      _valueBoxes.addAll(this.engine.collector.getUncollected(stmt.getUseBoxes()));
    }

    return _valueBoxes;
  }

  public Collection retrieveValueBoxesToTransformStmt(Stmt stmt)
  {
    Collection _valueBoxes = new HashSet(stmt.getUseAndDefBoxes());

    if (stmt.containsInvokeExpr()) {
      InvokeExpr _invokeExpr = stmt.getInvokeExpr();

      _valueBoxes.removeAll(_invokeExpr.getUseBoxes());

      if (_invokeExpr instanceof InstanceInvokeExpr) {
        _valueBoxes.add(((InstanceInvokeExpr)_invokeExpr).getBaseBox());
      }
    }

    return _valueBoxes;
  }

  private boolean considerMethodExitForCriteriaGeneration(SootMethod callee, boolean expr)
  {
    boolean _result = false;

    if (!(this.exitTransformedMethods.containsKey(callee))) {
      _result = true;
    } else if (expr) {
      Collection _temp = (Collection)MapUtils.getObject(this.exitTransformedMethods, callee, Collections.EMPTY_SET);
      Iterator _i = _temp.iterator();
      int _iEnd = _temp.size();

      for (int _iIndex = 0; (_iIndex < _iEnd) && (!(_result)); ++_iIndex) {
        Stmt _tail = (Stmt)_i.next();

        if (_tail instanceof ReturnStmt)
          _result |= !(this.engine.collector.hasBeenCollected(((ReturnStmt)_tail).getOpBox()));
        else if (_tail instanceof ThrowStmt) {
          _result |= !(this.engine.collector.hasBeenCollected(((ThrowStmt)_tail).getOpBox()));
        }
      }
    }

    if (LOGGER.isDebugEnabled()) {
      LOGGER.debug("Consider Method exit for method " + callee + " is " + _result);
    }

    return _result;
  }

  private void generateCriteriaForReceiverOfAt(Stmt callStmt, SootMethod caller)
  {
    if (LOGGER.isDebugEnabled()) {
      LOGGER.debug("generateCriteriaForReceiver(Stmt invocationStmt = " + callStmt + ", SootMethod caller = " + caller + 
        ", stack = " + this.engine.getCopyOfCallStackCache() + ") - BEGIN");
    }

    if (!(callStmt.getInvokeExpr().getMethod().isStatic())) {
      ValueBox _vBox = ((InstanceInvokeExpr)callStmt.getInvokeExpr()).getBaseBox();
      this.engine.generateExprLevelSliceCriterion(_vBox, callStmt, caller, true);
    }

    if (LOGGER.isDebugEnabled())
      LOGGER.debug("generateCriteriaForReceiver(), stack = " + this.engine.getCopyOfCallStackCache() + " - END");
  }

  private void processSuperInitInInit(SootMethod initMethod, BasicBlockGraph bbg)
  {
    if (LOGGER.isDebugEnabled()) {
      LOGGER.debug("processSuperInitInInit(SootMethod initMethod = " + initMethod + ", BasicBlockGraph bbg = " + bbg + 
        ", stack = " + this.engine.getCopyOfCallStackCache() + ") - BEGIN");
    }

    if ((initMethod.getName().equals("<init>")) && (initMethod.getDeclaringClass().hasSuperclass())) {
      CompleteUnitGraph _ug = new CompleteUnitGraph(initMethod.getActiveBody());
      SimpleLocalUses _sul = new SimpleLocalUses(_ug, new SimpleLocalDefs(_ug));
      List _uses = _sul.getUsesOf(bbg.getHead().getLeaderStmt());

      for (Iterator _i = _uses.iterator(); _i.hasNext(); ) {
        UnitValueBoxPair _ubp = (UnitValueBoxPair)_i.next();
        Stmt _stmt = (Stmt)_ubp.getUnit();

        if (_stmt instanceof InvokeStmt) {
          SootMethod _called = _stmt.getInvokeExpr().getMethod();

          if ((!(_called.getName().equals("<init>"))) || 
            (!(_called.getDeclaringClass().equals(initMethod.getDeclaringClass().getSuperclass())))) continue;
          this.engine.generateStmtLevelSliceCriterion(_stmt, initMethod, true);
          break;
        }
      }

    }

    if (LOGGER.isDebugEnabled())
      LOGGER.debug("processSuperInitInInit() - END");
  }

  private void processTailsOf(Collection callees, Stmt stmt, SootMethod caller, Closure closure)
  {
    if (LOGGER.isDebugEnabled()) {
      LOGGER.debug("processTailsOf(Collection callees = " + callees + ", Stmt stmt = " + stmt + 
        ", SootMethod caller = " + caller + ", Closure closure = " + closure + ", stack = " + 
        this.engine.getCopyOfCallStackCache() + ") - BEGIN");
    }

    BasicBlockGraphMgr _bbgMgr = this.engine.getBasicBlockGraphManager();

    for (Iterator _i = callees.iterator(); _i.hasNext(); ) {
      SootMethod _callee = (SootMethod)_i.next();

      if (considerMethodExitForCriteriaGeneration(_callee, closure == this.returnValueInclClosure)) {
        this.engine.collector.includeInSlice(_callee);

        if (_callee.isConcrete()) {
          BasicBlockGraph _calleeBasicBlockGraph = _bbgMgr.getBasicBlockGraph(_callee);
          ICallGraphInfo.CallTriple _callTriple = new ICallGraphInfo.CallTriple(caller, stmt, stmt.getInvokeExpr());
          this.engine.enterMethod(_callTriple);
          processSuperInitInInit(_callee, _calleeBasicBlockGraph);

          Collection _temp = new HashSet();

          for (Iterator _j = _calleeBasicBlockGraph.getSinks().iterator(); _j.hasNext(); ) {
            BasicBlockGraph.BasicBlock _bb = (BasicBlockGraph.BasicBlock)_j.next();
            Stmt _trailer = _bb.getTrailerStmt();
            closure.execute(new Pair(_trailer, _callee));
            _temp.add(_trailer);
          }
          this.exitTransformedMethods.put(_callee, _temp);
          this.engine.returnFromMethod();
        } else {
          InvokeExpr _invokeExpr = stmt.getInvokeExpr();

          for (int _j = _callee.getParameterCount() - 1; _j >= 0; --_j) {
            this.engine.generateExprLevelSliceCriterion(_invokeExpr.getArgBox(_j), stmt, caller, true);
          }
        }
      }
      recordCallInfoForProcessingArgsTo(stmt, caller, _callee);
    }
    generateCriteriaForReceiverOfAt(stmt, caller);

    if (LOGGER.isDebugEnabled())
      LOGGER.debug("processTailsOf() , stack = " + this.engine.getCopyOfCallStackCache() + "- END");
  }

  private void recordCallInfoForProcessingArgsTo(Stmt stmt, SootMethod caller, SootMethod callee)
  {
    Stack _stackClone = this.engine.getCopyOfCallStackCache();
    if (LOGGER.isDebugEnabled()) {
      LOGGER.debug("recordCallInfoForParameterProcessing(Stmt stmt = " + stmt + ", SootMethod caller = " + caller + 
        ", SootMethod callee = " + callee + ", stack = " + _stackClone + ") - BEGIN");
    }

    Triple _triple = new Triple(stmt, 
      caller, _stackClone);
    CollectionsUtilities.putIntoSetInMap(this.callee2callsites, callee, _triple);

    if (LOGGER.isDebugEnabled())
      LOGGER.debug("recordCallInfoForParameterProcessing() - END");
  }

  private class ReturnValueInclusionClosure
    implements Closure
  {
    public void execute(Object input)
    {
      Pair _pair = (Pair)input;
      Stmt _trailer = (Stmt)_pair.getFirst();
      SootMethod _callee = (SootMethod)_pair.getSecond();

      if (!(_trailer instanceof ReturnStmt)) {
        return;
      }
      BackwardSlicingPart.this.engine.generateExprLevelSliceCriterion(((ReturnStmt)_trailer).getOpBox(), _trailer, _callee, true);
    }
  }

  private class TailStmtInclusionClosure
    implements Closure
  {
    public void execute(Object input)
    {
      Pair _pair = (Pair)input;
      Stmt _stmt = (Stmt)_pair.getFirst();
      SootMethod _callee = (SootMethod)_pair.getSecond();
      BackwardSlicingPart.this.engine.generateStmtLevelSliceCriterion(_stmt, _callee, false);
    }
  }
}

/* Location:           D:\tool\banderaCLI-1.0a4\lib\indus\slicer.jar
 * Qualified Name:     edu.ksu.cis.indus.slicer.BackwardSlicingPart
 * Java Class Version: 5 (49.0)
 * JD-Core Version:    0.5.3
 */